﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using DomainModel;
using Repository.Abstract;
using Services.Abstract;

namespace Services.Concrete
{
    public class UserService : Service<User>, IUserService
    {
        private readonly IAuthenticationService _authenticationService;
        private readonly IUserRepository _userRepository;

        public UserService(IUserRepository userRepository, IAuthenticationService authenticationService,
                           IPrincipal currentUser)
            : base(userRepository, currentUser)
        {
            _userRepository = userRepository;
            _authenticationService = authenticationService;
        }

        public User GetUserByLogin(string login)
        {
            if (login == null || string.IsNullOrEmpty(login))
                throw new ArgumentNullException("login");

            return _userRepository.GetUserByLogin(login);
        }

        public List<User> GetUsers(Role role, bool approvedOnly = true)
        {
            return _userRepository.GetUsersByRole(role, approvedOnly).ToList();
        }

        public List<User> GetUsers(Group group, bool approvedOnly = true)
        {
            if (group == null)
                throw new ArgumentNullException("group");

            return _userRepository.GetUsersByGroup(group, approvedOnly).ToList();
        }

        public bool LoginIsBusy(string login)
        {
            if (login == null || string.IsNullOrEmpty(login))
                throw new ArgumentNullException("login");

            return _userRepository.GetUserByLogin(login) != null;
        }

        public bool EmailIsBusy(string email)
        {
            if (email == null || string.IsNullOrEmpty(email))
                throw new ArgumentNullException("email");

            return _userRepository.GetUserByEmail(email) != null;
        }

        public bool UserIsApproved(string login)
        {
            if (login == null || string.IsNullOrEmpty(login))
                throw new ArgumentNullException("login");

            User user = _userRepository.GetUserByLogin(login);
            return user.Approved;
        }

        public bool UserIsPraepostor(User student, Group group)
        {
            if (student == null)
                throw new ArgumentNullException("student");

            if (group == null)
                throw new ArgumentNullException("group");

            if (student.Role != Role.Student)
                return false;

            return group.Praepostor == student;
        }

        public bool UserIsCurator(User teacher, Group group)
        {
            if (teacher == null)
                throw new ArgumentNullException("teacher");
            if (group == null)
                throw new ArgumentNullException("group");

            if (teacher.Role != Role.Teacher)
                return false;

            return group.Curator == teacher;
        }

        public void Approve(User user)
        {
            if (!PermissionService.CanApproveUser(user))
                throw new Exception("Недостаточно прав для подтверждения регистрации этого пользователя.");

            if (user == null)
                throw new ArgumentNullException("user");

            if (user.Approved)
                return;

            user.Approved = true;
            SaveOrUpdate(user);
        }

        public int GetNotApprovedUsersCount()
        {
            return _userRepository.GetCountNotApprovedUsers();
        }

        public List<User> GetStudentsWithoutParent()
        {
            return _userRepository.GetStudentsWithoutParent().ToList();
        }

        public User GetParent(User user)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            return _userRepository.GetParent(user);
        }

        public bool CredentialsAreValid(string login, string password)
        {
            if (login == null || string.IsNullOrEmpty(login))
                throw new ArgumentNullException("login");

            if (password == null || string.IsNullOrEmpty(password))
                throw new ArgumentNullException("password");

            User user = _userRepository.GetUserByLogin(login);

            return user != null && user.PasswordHash.Equals(User.HashPassword(password));
        }

        public void SignIn(string login, string password, bool rememberMe)
        {
            if (string.IsNullOrEmpty(login) || string.IsNullOrWhiteSpace(login))
                throw new Exception("Необходимо ввести логин.");

            if (string.IsNullOrEmpty(password) || string.IsNullOrWhiteSpace(password))
                throw new Exception("Необходимо ввести пароль.");

            if (CurrentUser.Identity.IsAuthenticated)
                throw new Exception("Пользователь уже авторизирован.");

            if (!CredentialsAreValid(login, password))
                throw new Exception("Неверный логин или пароль.");

            if (!UserIsApproved(login))
                throw new Exception("Пользователь не подтвержден.");

            User user = _userRepository.GetUserByLogin(login);

            if (user == null)
                throw new Exception("Пользователь не найден.");

            _authenticationService.SignIn(user, rememberMe);
        }

        public void SignOut()
        {
            if (!CurrentUser.Identity.IsAuthenticated)
                throw new Exception("Пользователь не авторизирован.");

            _authenticationService.SignOut();
        }

        public new void SaveOrUpdate(User entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            if (string.IsNullOrEmpty(entity.UserProfile.Fio))
                throw new Exception("Необходимо ввести ФИО.");

            if (string.IsNullOrEmpty(entity.Login))
                throw new Exception("Необходимо ввести логин.");

            if (string.IsNullOrEmpty(entity.PasswordHash))
                throw new Exception("Необходимо ввести пароль.");

            if (string.IsNullOrEmpty(entity.Email))
                throw new Exception("Необходимо ввести e-mail.");

            if (entity.Role == Role.None)
                throw new Exception("Необходимо выбрать роль.");

            if (entity.Role == Role.Parent)
            {
                if (entity.Child == null)
                    throw new Exception("У родителя не выбран ребенок.");
            }
            else
            {
                entity.Child = null;
            }

            if (entity.Role == Role.Student)
            {
                if (entity.Group == null)
                    throw new Exception("У студента не выбрана группа.");
            }
            else
            {
                entity.Group = null;
            }

            User u = _userRepository.GetUserByLogin(entity.Login);
            if (u != null && u.Id != entity.Id)
                throw new Exception("Логин занят.");

            u = _userRepository.GetUserByLogin(entity.Login);
            if (u != null && u.Id != entity.Id)
                throw new Exception("E-mail занят.");

            base.SaveOrUpdate(entity);
        }

    }
}